home *** CD-ROM | disk | FTP | other *** search
/ BMUG PD-ROM 1995 Fall / PD-ROM F95.toast / Programming / Programming Utilities / ThreadLibrary 1.0 ƒ / Source / Demos / ThreadsTest / ThreadsTest.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-31  |  18.7 KB  |  639 lines  |  [TEXT/MPCC]

  1. /* See the file Distribution for distribution terms.
  2.     Copyright (c) 1994-1995 Ari Halberstadt */
  3.  
  4. /* A simple program to test my threads library. First a test is run using
  5.     Apple's Thread Manager. Then, the same test is run using my Thread Library.
  6.     In each test, a dialog is displayed with two counters, each incremented in
  7.     its own thread. The display of the counters is updated once a second from
  8.     a third thread. The "count1" line contains the value of the first counter,
  9.     the "count2" line contains the value of the second counter. The "elapsed"
  10.     line contains the number of ticks between updates (should be close to 60
  11.     ticks). The "remaining" line shows the number of seconds until the test
  12.     is finished (about 45 seconds). Click the "Quit" button to quit the program.
  13.     
  14.     950531 aih     - removed Stop button
  15.     950403 aih     - uses new interface that makes Thread Library act like
  16.                         Thread Manager
  17.                      - any number of test dialogs can be created (no globals)
  18.     940907 aih     - uses ThreadEndAll to dispose of all threads
  19.     940315 aih     - fixed handling of update events and window dragging;
  20.                         the previous releases of this test application just
  21.                         barely worked by a lucky accident
  22.                      - added debugging commands for TMON and MacsBug
  23.                      - added a few Gestalt utility routines
  24.                      - remaining time is shown as seconds instead of ticks
  25.                      - increased test time from 30 seconds to 45 seconds
  26.     940301 aih     - removed test for system version
  27.     940225 aih     - uses WaitNextEvent instead of GetNextEvent to demonstrate
  28.                         use of ThreadYieldInterval; had to add a bunch of code to
  29.                         test for presence of WNE trap
  30.     940217 aih     - release 1.0d2.1
  31.                      - added test using Thread Manager
  32.                      - added stop button
  33.                      - added SetPort once every time through event loop; thanks
  34.                         to Matthew Xavier Mora <mxmora@unix.sri.com> for suggesting
  35.                         this.
  36.                      - added note about update problem
  37.                      - added a couple of dialog utility functions
  38.     940217 aih     - release 1.0d2
  39.                      - removed exception handlers
  40.     940214 aih     - thread serial numbers are now used when disposing of the
  41.                         threads to prevent the possibility of disposing of a
  42.                         thread more than once
  43.                      - added some more comments to make it easier to follow this
  44.                          program
  45.     940212 aih     - uses ThreadPtr instead of ThreadHandle
  46.                      - uses IsDialogEvent/DialogSelect instead of ModalDialog,
  47.                          and a modeless dialog is used
  48.                      - set size resource flags so it's multifinder aware and
  49.                          can be run in the background
  50.                      - with the above changes, ran about 3 times faster on my mac;
  51.                         i think most of the slowness of the previous version was
  52.                          due to the overhead of ModalDialog and EventAvail.
  53.                      - added error alert
  54.     940210 aih     - created */
  55.  
  56. #include <Desk.h>
  57. #include <Dialogs.h>
  58. #include <Events.h>
  59. #include <Fonts.h>
  60. #include <GestaltEqu.h>
  61. #include <LowMem.h>
  62. #include <Memory.h>
  63. #include <Menus.h>
  64. #include <QuickDraw.h>
  65. #include <SegLoad.h>
  66. #include <OSEvents.h>
  67. #include <OSUtils.h>
  68. #include <Resources.h>
  69. #include <TextUtils.h>
  70. #include <ToolUtils.h>
  71. #include <Threads.h>
  72. #include <Traps.h>
  73. #include "ThreadLibraryManager.h"
  74.  
  75. /*----------------------------------------------------------------------------*/
  76. /* global definitions and declarations */
  77. /*----------------------------------------------------------------------------*/
  78.  
  79. /* When DEBUGGER_CHECKS is defined as 1 then commands for a low-level
  80.     debugger (either TMON or MacsBug) are executed on startup to enable
  81.     useful debugging options:
  82.         
  83.         - the integrity of the heap will be checked after every trap that
  84.         could move or purge memory
  85.         
  86.         - if discipline is installed, then it will be enabled.
  87.         
  88.     While these tests are very useful when debugging, they will make the
  89.     test application run very slowly. Since the THINK C debugger intercepts
  90.     the DebugStr trap, the TMON commands can only be executed if a
  91.     compiled application is run without the THINK C debugger. */
  92. #ifndef DEBUGGER_CHECKS
  93.     #define DEBUGGER_CHECKS    (0)
  94. #endif
  95.  
  96. #define DIALOG_ID                (128)
  97. #define ERROR_ALERT_ID        (256)
  98. #define METHOD_ALERT_ID        (257)
  99. #define SECTICKS                (60L)
  100. #define RUNTICKS                (SECTICKS * 60L)
  101.  
  102. /* dialog items */
  103. enum {
  104.     iQuit = 5,
  105.     iType,
  106.     iCount1,
  107.     iCount2,
  108.     iElapsed,
  109.     iRemaining,
  110.     iLast
  111. };
  112.  
  113. /* alert items */
  114. enum {
  115.     iMethodThreadLibrary = 1,
  116.     iMethodThreadManager
  117. };
  118.  
  119. typedef OSErr (*NewThreadProcType)(ThreadStyle threadStyle, ThreadEntryProcPtr threadEntry, void *threadParam, Size stackSize, ThreadOptions options, void **threadResult, ThreadID *threadMade);
  120. typedef OSErr (*DisposeThreadProcType)(ThreadID threadToDump, void *threadResult, Boolean recycleThread);
  121.  
  122. /* info for a test */
  123. typedef struct {
  124.     DialogPtr dlg;
  125.     long counter1;
  126.     long counter2;
  127.     ThreadID thread_counter1;
  128.     ThreadID thread_counter2;
  129.     ThreadID thread_display;
  130.     DisposeThreadProcType dispose;
  131. } ThreadTestType;
  132.  
  133. /* true if quitting program */
  134. static Boolean gQuit;
  135.  
  136. /*----------------------------------------------------------------------------*/
  137. /* standard Macintosh initializations */
  138. /*----------------------------------------------------------------------------*/
  139.  
  140. /* initialize application heap */
  141. static void HeapInit(long stack, short masters)
  142. {
  143.     SetApplLimit(GetApplLimit() - stack);
  144.     MaxApplZone();
  145.     while (masters-- > 0)
  146.         MoreMasters();
  147. }
  148.  
  149. /* initialize managers */
  150. static void ManagersInit(void)
  151. {
  152.     EventRecord event;
  153.     short i;
  154.     
  155.     /* standard initializations */
  156.     InitGraf((Ptr) &qd.thePort);
  157.     InitFonts();
  158.     InitWindows();
  159.     InitMenus();
  160.     TEInit();
  161.     InitDialogs(0);
  162.     FlushEvents(everyEvent, 0);
  163.     InitCursor();
  164.     
  165.     /* so first window will be frontmost */
  166.     for (i = 0; i < 4; i++)
  167.         EventAvail(everyEvent, &event);
  168. }
  169.  
  170. /*----------------------------------------------------------------------------*/
  171. /* functions for determining features of the operating environment */
  172. /*----------------------------------------------------------------------------*/
  173.  
  174. /* return number of toolbox traps */
  175. static short TrapNumToolbox(void)
  176. {
  177.     short result = 0;
  178.     
  179.     if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
  180.         result = 0x0200;
  181.     else
  182.         result = 0x0400;
  183.     return(result);
  184. }
  185.  
  186. /* return the type of the trap */
  187. static TrapType TrapTypeGet(short trap)
  188. {
  189.     return((trap & 0x0800) > 0 ? ToolTrap : OSTrap);
  190. }
  191.  
  192. /* true if the trap is available  */
  193. static Boolean TrapAvailable(short trap)
  194. {
  195.     TrapType type;
  196.     
  197.     type = TrapTypeGet(trap);
  198.     if (type == ToolTrap) {
  199.         trap &= 0x07FF;
  200.         if (trap >= TrapNumToolbox())
  201.             trap = _Unimplemented;
  202.     }
  203.     return(NGetTrapAddress(trap, type) != NGetTrapAddress(_Unimplemented, ToolTrap));
  204. }
  205.  
  206. /* true if gestalt trap is available */
  207. static Boolean GestaltAvailable(void)
  208. {
  209.     static Boolean initialized, available;
  210.     
  211.     if (! initialized) {
  212.         available = TrapAvailable(0xA1AD);
  213.         initialized = true;
  214.     }
  215.     return(available);
  216. }
  217.  
  218. /* return gestalt response, or 0 if error or gestalt not available */
  219. static long GestaltResponse(OSType selector)
  220. {
  221.     long response, result;
  222.     
  223.     response = result = 0;
  224.     if (GestaltAvailable() && Gestalt(selector, &response) == noErr)
  225.         result = response;
  226.     return(result);
  227. }
  228.  
  229. /* test bit in gestalt response; false if error or gestalt not available */
  230. static Boolean GestaltBitTst(OSType selector, short bit)
  231. {
  232.     return((GestaltResponse(selector) & (1 << bit)) != 0);
  233. }
  234.  
  235. /* true if the WaitNextEvent trap is available */
  236. static Boolean MacHasWNE(void)
  237. {
  238.     static Boolean initialized;
  239.     static Boolean wne;
  240.     
  241.     if (! initialized) {
  242.         /* do only once for efficiency */
  243.         wne = TrapAvailable(_WaitNextEvent);
  244.         initialized = true;
  245.     }
  246.     return(wne);
  247. }
  248.  
  249. /* true if TMON is installed */
  250. static Boolean MacHasTMON(void)
  251. {
  252.     /*    See "TMON Professional Reference", section 9.4.1 "Testing for
  253.         Monitor Presence" (1990, ICOM Simulations, Inc). This only
  254.         works with version 3.0 or later of TMON. */
  255.     return(GestaltResponse('TMON') != 0);
  256. }
  257.  
  258. /*----------------------------------------------------------------------------*/
  259. /* debug utilities */
  260. /*----------------------------------------------------------------------------*/
  261.  
  262. /* Execute debugger commands to enable discipline and heap check
  263.     on all traps in this application. These commands make the program
  264.     run *very* slowly, but the commands are also very useful for
  265.     finding bugs. */
  266. static void DebuggerChecksOn(void)
  267. {
  268.     #if DEBUGGER_CHECKS
  269.         if (MacHasTMON()) {
  270.             /* use TMON */
  271.             DebugStr((StringPtr) "\p™traps /check=1");
  272.             DebugStr((StringPtr) "\p™traps /purge=1");
  273.             DebugStr((StringPtr) "\p™traps /scramble=1");
  274.             DebugStr((StringPtr) "\p™traps set discipline heap ..:inThisapp");
  275.         }
  276.         else {
  277.             /* assume MacsBug */
  278.             DebugStr((StringPtr) "\p; dsca on; athca; g");
  279.         }
  280.     #endif /* DEBUGGER_CHECKS */
  281. }
  282.  
  283. /* Execute debugger commands to disable the debug options installed
  284.     on startup. */
  285. static void DebuggerChecksOff(void)
  286. {
  287.     #if DEBUGGER_CHECKS
  288.         DebugStr((StringPtr) "\p™traps clear ..//; dsca off; atc; g");
  289.     #endif /* DEBUGGER_CHECKS */
  290. }
  291.  
  292. /*----------------------------------------------------------------------------*/
  293. /* event utilities */
  294. /*----------------------------------------------------------------------------*/
  295.  
  296. /* Call GetNextEvent or WaitNextEvent, depending on which one is available.
  297.     The parameters to this function are identical to those to WaitNextEvent.
  298.     If GetNextEvent is called the extra parameters are ignored. */
  299. static Boolean EventGet(short mask, EventRecord *event,
  300.     long sleep, RgnHandle cursor)
  301. {
  302.     Boolean result = false;
  303.  
  304.     if (MacHasWNE())
  305.         result = WaitNextEvent(mask, event, sleep, cursor);
  306.     else {
  307.         SystemTask();
  308.         result = GetNextEvent(mask, event);
  309.     }
  310.     if (! result) {
  311.         /* make sure it's a null event, even if the system thinks otherwise, e.g.,
  312.             some desk accessory events (see comment in TransSkell event loop) */
  313.         event->what = nullEvent;
  314.     }
  315.     return(result);
  316. }
  317.  
  318. /*----------------------------------------------------------------------------*/
  319. /* dialog utilities */
  320. /*----------------------------------------------------------------------------*/
  321.  
  322. /* set the text of the dialog item */
  323. static void SetDText(DialogPtr dlg, short item, const Str255 str)
  324. {
  325.     short type;
  326.     Handle hitem;
  327.     Rect box;
  328.  
  329.     GetDialogItem(dlg, item, &type, &hitem, &box);
  330.     SetDialogItemText(hitem, str);
  331. }
  332.  
  333. /* set the text of the dialog item to the number */
  334. static void SetDNum(DialogPtr dlg, short item, long num)
  335. {
  336.     Str255 str;
  337.  
  338.     NumToString(num, str);
  339.     SetDText(dlg, item, str);
  340. }
  341.  
  342. /*----------------------------------------------------------------------------*/
  343. /* thread utilities */
  344. /*----------------------------------------------------------------------------*/
  345.  
  346. /* yield to other threads */
  347. static void Yield(void)
  348. {
  349.     YieldToAnyThread();
  350.     TLMYieldToAnyThread();
  351. }
  352.  
  353. /*----------------------------------------------------------------------------*/
  354. /* The thread entry point functions. Every thread other than the main thread
  355.     has an entry point function. When the thread is first run the entry point
  356.     function is called. When the entry point function returns the thread is
  357.     disposed of. We run these threads in infinite loops, and call ThreadYield
  358.     after each iteration to allow other threads to execute. The threads will
  359.     be terminated when the application exits. */
  360. /*----------------------------------------------------------------------------*/
  361.  
  362. /* Thread to increment a counter. This is an extremely inefficient
  363.     method to increment a counter. Even though context switches for threads
  364.     are fairly fast, they are still about 2 orders of magnitude slower than
  365.     the few instructions needed to execute a loop. This thread is not meant
  366.     to be efficient, and is merely meant to illustrate how threads
  367.     can be used. In a real application you would want threads to do much
  368.     more in each iteration. */
  369. static pascal void *thread_count(void *data)
  370. {
  371.     long *counter = data;
  372.  
  373.     for (;;) {
  374.         ++*counter;
  375.         Yield();
  376.     }
  377.     return(NULL);
  378. }
  379.  
  380. /* Thread to update the display of the counters in the dialog. */
  381. static pascal void *thread_display(void *data)
  382. {
  383.     ThreadTestType *test = data;
  384.     long wake;
  385.     long ticks;
  386.     
  387.     for (;;) {
  388.         SetDNum(test->dlg, iCount1, test->counter1);
  389.         SetDNum(test->dlg, iCount2, test->counter2);
  390.         ticks = TickCount();
  391.         wake = TickCount() + SECTICKS;
  392.         while (TickCount() < wake)
  393.             Yield();
  394.         SetDNum(test->dlg, iElapsed, TickCount() - ticks);
  395.     }
  396.     return(NULL);
  397. }
  398.  
  399. /*----------------------------------------------------------------------------*/
  400. /* Thread creation and destruction functions. For convenience, we create all
  401.     of the threads in one function, and dispose of them in another function.
  402.     Threads can be created at any time, though the main thread, which is
  403.     created with ThreadBeginMain, must be the first thread created and the
  404.     last thread disposed of. */
  405. /*----------------------------------------------------------------------------*/
  406.  
  407. /* display error number in an alert */
  408. static void ErrorAlert(OSErr err)
  409. {
  410.     Str255 str;
  411.     
  412.     NumToString(err, str);
  413.     ParamText(str, (StringPtr) "\p", (StringPtr) "\p", (StringPtr) "\p");
  414.     StopAlert(ERROR_ALERT_ID, NULL);
  415. }
  416.  
  417. /* display an error if non-zero error and then exit */
  418. static void FailOSErr(OSErr err)
  419. {
  420.     if (err) {
  421.         ErrorAlert(err);
  422.         ExitToShell();
  423.     }
  424. }
  425.  
  426. /* display an error if nil and then exit */
  427. static void FailNIL(void *p)
  428. {
  429.     if (! p)
  430.         FailOSErr(MemError() ? MemError() : memFullErr);
  431. }
  432.  
  433. /* display an error if nil and then exit */
  434. static void FailNILRes(void *p)
  435. {
  436.     if (! p)
  437.         FailOSErr(ResError() ? ResError() : resNotFound);
  438. }
  439.  
  440. /* callback for 68k inline function */
  441. static OSErr TMNewThread(ThreadStyle threadStyle, ThreadEntryProcPtr threadEntry, void *threadParam, Size stackSize, ThreadOptions options, void **threadResult, ThreadID *threadMade)
  442. {
  443.     return(NewThread(threadStyle, threadEntry, threadParam, stackSize, options, threadResult, threadMade));
  444. }
  445.  
  446. /* callback for 68k inline function */
  447. static OSErr TMDisposeThread(ThreadID threadToDump, void *threadResult, Boolean recycleThread)
  448. {
  449.     return(DisposeThread(threadToDump, threadResult, recycleThread));
  450. }
  451.  
  452. /* Create a new test. */
  453. static ThreadTestType *ThreadTestBegin(NewThreadProcType new, DisposeThreadProcType dispose,
  454.     const unsigned char *prompt, short ntests)
  455. {
  456.     const short offset = 20;
  457.     ThreadTestType *test;
  458.  
  459.     test = (ThreadTestType *) NewPtrClear(sizeof(ThreadTestType));
  460.     FailNIL(test);
  461.     test->dispose = dispose;
  462.     test->dlg = GetNewDialog(DIALOG_ID, NULL, (WindowPtr) -1);
  463.     FailNILRes(test->dlg);
  464.     FailOSErr(new(    kCooperativeThread, thread_count, &test->counter1, 0,
  465.                         kCreateIfNeeded, NULL, &test->thread_counter1));
  466.     FailOSErr(new(    kCooperativeThread, thread_count, &test->counter2, 0,
  467.                         kCreateIfNeeded, NULL, &test->thread_counter1));
  468.     FailOSErr(new(    kCooperativeThread, thread_display, test, 0,
  469.                         kCreateIfNeeded, NULL, &test->thread_display));
  470.     SetDText(test->dlg, iType, prompt);
  471.     SetWRefCon(test->dlg, (long) test);
  472.     MoveWindow(test->dlg,
  473.         qd.screenBits.bounds.left + (ntests + 1) * offset,
  474.         qd.screenBits.bounds.top + 2 * GetMBarHeight() + (ntests + 1) * offset, false);
  475.     ShowWindow(test->dlg);
  476.     return(test);
  477. }
  478.  
  479. /* Dispose of the test. */
  480. static void ThreadTestDispose(ThreadTestType *test)
  481. {
  482.     if (test) {
  483.         if (test->dlg) DisposeDialog(test->dlg);
  484.         if (test->thread_counter1) test->dispose(test->thread_counter1, NULL, false);
  485.         if (test->thread_counter2) test->dispose(test->thread_counter2, NULL, false);
  486.         if (test->thread_display) test->dispose(test->thread_display, NULL, false);
  487.         DisposePtr((Ptr) test);
  488.     }
  489. }
  490.  
  491. /*----------------------------------------------------------------------------*/
  492. /* Running the test program. */
  493. /*----------------------------------------------------------------------------*/
  494.  
  495. /* handle the event, return true when should exit the event loop */
  496. static Boolean DoEvent(EventRecord *event)
  497. {
  498.     WindowPtr window;    /* for handling window events */
  499.     DialogPtr dlgHit;    /* dialog for which event was generated */
  500.     short itemHit;        /* item selected from dialog */
  501.     Rect dragRect;        /* rectangle in which to drag windows */
  502.     ThreadTestType *test;
  503.  
  504.     switch (event->what) {
  505.     case mouseDown:
  506.         switch (FindWindow(event->where, &window)) {
  507.         case inDrag:
  508.             dragRect = (**GetGrayRgn()).rgnBBox;
  509.             DragWindow(window, event->where, &dragRect);
  510.             break;
  511.         case inSysWindow:
  512.             SystemClick(event, window);
  513.             break;
  514.         case inContent:
  515.             if (window != FrontWindow()) {
  516.                 SelectWindow(window);
  517.                 event->what = nullEvent;
  518.             }
  519.         }
  520.         break;
  521.     case nullEvent:
  522.         /* Yield to other threads only on null events, since whenever
  523.             there's an event pending in the queue the main thread will
  524.             be activated, so the call to Yield would be an expensive
  525.             "no op". */
  526.         Yield();
  527.         break;
  528.     }
  529.     
  530.     /* handle a dialog event */
  531.     if (IsDialogEvent(event) && DialogSelect(event, &dlgHit, &itemHit)) {
  532.         
  533.         /* handle a click in one of the dialog's buttons */
  534.         test = (ThreadTestType *) GetWRefCon(dlgHit);
  535.         if (test) {
  536.             switch (itemHit) {
  537.             case iQuit:
  538.                 gQuit = true;
  539.                 break;
  540.             }
  541.         }
  542.     }
  543.     return(gQuit);
  544. }
  545.         
  546. /* get and handle the next event; return true to exit event loop */
  547. static Boolean GetAndDoEvent(void)
  548. {
  549.     EventRecord event;
  550.  
  551.     SetCursor(&qd.arrow);
  552.     (void) EventGet(everyEvent, &event, 0, NULL);
  553.     return(DoEvent(&event));
  554. }
  555.  
  556. /* create the dialog and run the program */
  557. static void Run(ThreadTestType *tests[], short ntests)
  558. {
  559.     long remainingUpdate;    /* when to update the remaining time counter */
  560.     long nextEvent;            /* when to check for another event */
  561.     long whenToStop;            /* when to stop the test */
  562.     short i;
  563.  
  564.     /* run until user stops or quits or we time out */
  565.     remainingUpdate = nextEvent = 0;
  566.     whenToStop = TickCount() + RUNTICKS;
  567.     while (! GetAndDoEvent() && TickCount() < whenToStop) {
  568.     
  569.         /* every second update display of time remaining */
  570.         if (TickCount() >= remainingUpdate) {
  571.             for (i = 0; i < ntests; i++)
  572.                 SetDNum(tests[i]->dlg, iRemaining, (whenToStop - TickCount()) / SECTICKS);
  573.             remainingUpdate = TickCount() + SECTICKS;
  574.         }
  575.  
  576.         /* don't call WNE too often */
  577.         while (TickCount() < nextEvent)
  578.             Yield();
  579.         nextEvent = TickCount() + 5;
  580.     }
  581. }
  582.  
  583. /* create and run several tests */
  584. static void CreateAndRun(NewThreadProcType new, DisposeThreadProcType dispose,
  585.     const unsigned char *prompt)
  586. {
  587.     const short ntests = 4;
  588.     ThreadTestType *tests[4];
  589.     short i;
  590.  
  591.     if (! gQuit) {
  592.  
  593.         /* create tests */
  594.         for (i = 0; i < ntests; i++)
  595.             tests[i] = ThreadTestBegin(new, dispose, prompt, i);
  596.     
  597.         /* run tests */
  598.         Run(tests, ntests);
  599.     
  600.         /* dispose of tests */
  601.         for (i = 0; i < ntests; i++)
  602.             ThreadTestDispose(tests[i]);
  603.     }
  604. }
  605.  
  606. void main(void)
  607. {
  608.     long threadsAttr;
  609.     short method;
  610.  
  611.     /* enable extra checks */
  612.     DebuggerChecksOn();
  613.     
  614.     /* standard initializations */
  615.     HeapInit(0, 4);
  616.     ManagersInit();
  617.  
  618.     /* select method */
  619.     method = iMethodThreadLibrary;
  620.     if (Gestalt(gestaltThreadMgrAttr, &threadsAttr) == noErr &&
  621.          (threadsAttr & (1<<gestaltThreadMgrPresent)) != 0)
  622.     {
  623.         method = NoteAlert(METHOD_ALERT_ID, NULL);
  624.     }
  625.  
  626.     /* run tests */
  627.     switch (method) {
  628.     case iMethodThreadLibrary:
  629.         CreateAndRun(TLMNewThread, TLMDisposeThread, (StringPtr) "\pUsing Thread LIBRARY");
  630.         break;
  631.     case iMethodThreadManager:
  632.         CreateAndRun(TMNewThread, TMDisposeThread, (StringPtr) "\pUsing Thread MANAGER");
  633.         break;
  634.     }
  635.  
  636.     /* disable extra checks */
  637.     DebuggerChecksOff();
  638. }
  639.